
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>【Prometheus】Exporter详解</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>在 <a href="https://andyoung.blog.csdn.net/article/details/122040410" rel="nofollow">【Prometheus】 Prometheus 入门到实战搭建监控系统 </a>中为了采集主机的监控样本数据，我们在主机上安装了一个Node Exporter程序，该程序对外暴露了一个用于获取当前监控样本数据的HTTP访问地址。这样的一个程序称为Exporter，Exporter的实例称为一个Target。Prometheus通过轮询的方式定时从这些Target中获取监控数据样本，并且存储在数据库当中。 在这一章节当中我们将重点讨论这些用于获取特定目标监控样本数据的程序Exporter。</p> 
<h2><a id="Exporter_2"></a>Exporter是什么</h2> 
<p>广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target，如下所示，Prometheus通过轮询的方式定期从这些target中获取样本数据:</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/fccb2dff2403007910f50ac950ff159c.png" alt="Exporter"></p> 
<h2><a id="Exporter_10"></a>Exporter的来源</h2> 
<p>从Exporter的来源上来讲，主要分为两类：</p> 
<ul><li>社区提供的</li></ul> 
<p>Prometheus社区提供了丰富的Exporter实现，涵盖了从基础设施，中间件以及网络等各个方面的监控功能。这些Exporter可以实现大部分通用的监控需求。下表列举一些社区中常用的Exporter：</p> 
<table><thead><tr><th>范围</th><th>常用Exporter</th></tr></thead><tbody><tr><td>数据库</td><td>MySQL Exporter, Redis Exporter, MongoDB Exporter, MSSQL Exporter等</td></tr><tr><td>硬件</td><td>Apcupsd Exporter，IoT Edison Exporter， IPMI Exporter, Node Exporter等</td></tr><tr><td>消息队列</td><td>Beanstalkd Exporter, Kafka Exporter, NSQ Exporter, RabbitMQ Exporter等</td></tr><tr><td>存储</td><td>Ceph Exporter, Gluster Exporter, HDFS Exporter, ScaleIO Exporter等</td></tr><tr><td>HTTP服务</td><td>Apache Exporter, HAProxy Exporter, Nginx Exporter等</td></tr><tr><td>API服务</td><td>AWS ECS Exporter， Docker Cloud Exporter, Docker Hub Exporter, GitHub Exporter等</td></tr><tr><td>日志</td><td>Fluentd Exporter, Grok Exporter等</td></tr><tr><td>监控系统</td><td>Collectd Exporter, Graphite Exporter, InfluxDB Exporter, Nagios Exporter, SNMP Exporter等</td></tr><tr><td>其它</td><td>Blockbox Exporter, JIRA Exporter, Jenkins Exporter， Confluence Exporter等</td></tr></tbody></table> 
<ul><li>用户自定义的</li></ul> 
<p>除了直接使用社区提供的Exporter程序以外，用户还可以基于Prometheus提供的Client Library创建自己的Exporter程序，目前Promthues社区官方提供了对以下编程语言的支持：Go、Java/Scala、Python、Ruby。同时还有第三方实现的如：Bash、C++、Common Lisp、Erlang,、Haskeel、Lua、Node.js、PHP、Rust等。</p> 
<h2><a id="Exporter_34"></a>Exporter的运行方式</h2> 
<p>从Exporter的运行方式上来讲，又可以分为：</p> 
<ul><li>独立使用的</li></ul> 
<p>以我们已经使用过的Node Exporter为例，由于操作系统本身并不直接支持Prometheus，同时用户也无法通过直接从操作系统层面上提供对Prometheus的支持。因此，用户只能通过独立运行一个程序的方式，通过操作系统提供的相关接口，将系统的运行状态数据转换为可供Prometheus读取的监控数据。 除了Node Exporter以外，比如MySQL Exporter、Redis Exporter等都是通过这种方式实现的。 这些Exporter程序扮演了一个中间代理人的角色。</p> 
<ul><li>集成到应用中的</li></ul> 
<p>为了能够更好的监控系统的内部运行状态，有些开源项目如Kubernetes，ETCD等直接在代码中使用了Prometheus的Client Library，提供了对Prometheus的直接支持。这种方式打破的监控的界限，让应用程序可以直接将内部的运行状态暴露给Prometheus，适合于一些需要更多自定义监控指标需求的项目。</p> 
<h2><a id="Exporter_46"></a>Exporter规范</h2> 
<p>所有的Exporter程序都需要按照Prometheus的规范，返回监控的样本数据。以Node Exporter为例，当访问/metrics地址时会返回以下内容：</p> 
<pre><code># HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="idle"} 362812.7890625
# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 3.0703125
</code></pre> 
<p>这是一种基于文本的格式规范，在Prometheus 2.0之前的版本还支持Protocol buffer规范。相比于Protocol buffer文本具有更好的可读性，以及跨平台性。Prometheus 2.0的版本也已经不再支持Protocol buffer，这里就不对Protocol buffer规范做详细的阐述。</p> 
<p>Exporter返回的样本数据，主要由三个部分组成：样本的一般注释信息（HELP），样本的类型注释信息（TYPE）和样本。Prometheus会对Exporter响应的内容逐行解析：</p> 
<p>如果当前行以# HELP开始，Prometheus将会按照以下规则对内容进行解析，得到当前的指标名称以及相应的说明信息：</p> 
<pre><code># HELP &lt;metrics_name&gt; &lt;doc_string&gt;
</code></pre> 
<p>如果当前行以# TYPE开始，Prometheus会按照以下规则对内容进行解析，得到当前的指标名称以及指标类型:</p> 
<pre><code># TYPE &lt;metrics_name&gt; &lt;metrics_type&gt;
</code></pre> 
<p>TYPE注释行必须出现在指标的第一个样本之前。如果没有明确的指标类型需要返回为untyped。 除了# 开头的所有行都会被视为是监控样本数据。 每一行样本需要满足以下格式规范:</p> 
<pre><code>metric_name [
  "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]
</code></pre> 
<p>其中metric_name和label_name必须遵循PromQL的格式规范要求。value是一个float格式的数据，timestamp的类型为int64（从1970-01-01 00:00:00以来的毫秒数），timestamp为可选默认为当前时间。具有相同metric_name的样本必须按照一个组的形式排列，并且每一行必须是唯一的指标名称和标签键值对组合。</p> 
<p>需要特别注意的是对于histogram和summary类型的样本。需要按照以下约定返回样本数据：</p> 
<ul><li> <p>类型为summary或者histogram的指标x，该指标所有样本的值的总和需要使用一个单独的x_sum指标表示。</p> </li><li> <p>类型为summary或者histogram的指标x，该指标所有样本的总数需要使用一个单独的x_count指标表示。</p> </li><li> <p>对于类型为summary的指标x，其不同分位数quantile所代表的样本，需要使用单独的x{quantile=“y”}表示。</p> </li><li> <p>对于类型histogram的指标x为了表示其样本的分布情况，每一个分布需要使用x_bucket{le=“y”}表示，其中y为当前分布的上位数。同时必须包含一个样本x_bucket{le="+Inf"}，并且其样本值必须和x_count相同。</p> </li><li> <p>对于histogram和summary的样本，必须按照分位数quantile和分布le的值的递增顺序排序。</p> </li></ul> 
<p>以下是类型为histogram和summary的样本输出示例：</p> 
<pre><code># A histogram, which has a pretty complex representation in the text format:
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320

# Finally a summary, which has a complex representation, too:
# HELP rpc_duration_seconds A summary of the RPC duration in seconds.
# TYPE rpc_duration_seconds summary
rpc_duration_seconds{quantile="0.01"} 3102
rpc_duration_seconds{quantile="0.05"} 3272
rpc_duration_seconds{quantile="0.5"} 4773
rpc_duration_seconds_sum 1.7560473e+07
rpc_duration_seconds_count 2693
</code></pre> 
<p>对于某些Prometheus还没有提供支持的编程语言，用户只需要按照以上规范返回响应的文本数据即可。</p> 
<h2><a id="_124"></a>指定样本格式的版本</h2> 
<p>在Exporter响应的HTTP头信息中，可以通过Content-Type指定特定的规范版本，例如：</p> 
<pre><code>HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 2906
Content-Type: text/plain; version=0.0.4
Date: Sat, 17 Mar 2018 08:47:06 GMT
</code></pre> 
<p>其中version用于指定Text-based的格式版本，当没有指定版本的时候，默认使用最新格式规范的版本。同时HTTP响应头还需要指定压缩格式为gzip。</p> 
<h2><a id="Exporter_138"></a><strong>常用Exporter</strong></h2> 
<h3><a id="MySQLMySQLD_Exporter_140"></a><strong>监控MySQL运行状态：MySQLD Exporter</strong></h3> 
<h4><a id="MySQLD_Exporter_142"></a>部署MySQLD Exporter</h4> 
<ul><li> <p>新建一个用户以及提添加权限</p> <pre><code>CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'XXXXXXXX';
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'exporter'@'localhost';
GRANT SELECT ON performance_schema.* TO 'exporter'@'localhost';
</code></pre> </li></ul> 
<p>为了简化测试环境复杂度，这里使用Docker Compose定义并启动MySQL以及MySQLD Exporter：</p> 
<pre><code>version: '3'
services:
  mysql:
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=database
  mysqlexporter:
    image: prom/mysqld-exporter
    ports:
      - "9104:9104"
    environment:
      - DATA_SOURCE_NAME=exporter:password@(hostname:port)/database
</code></pre> 
<p>这里通过环境变量DATA_SOURCE_NAME方式定义监控目标。使用Docker Compose启动测试用的MySQL实例以及MySQLD Exporter:</p> 
<pre><code>docker-compose up -d
</code></pre> 
<p>或者使用k8s</p> 
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysqld-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysqld-exporter
  template:
    metadata:
      labels:
        app: mysqld-exporter
    spec:
      containers:
        - name: mysqld-exporter
          image: prom/mysqld-exporter
          ports:
            - containerPort: 9104
              protocol: TCP
          env:
            - name: DATA_SOURCE_NAME
              value: 'exporter:password@(hostname:port)/database'
</code></pre> 
<p>启动完成后，可以通过以下命令登录到MySQL容器当中，并执行MySQL相关的指令：</p> 
<pre><code>$ docker exec -it &lt;mysql_container_id&gt; mysql -uroot -ppassword
mysql&gt;
</code></pre> 
<p>可以通过http://localhost:9104访问MySQLD Exporter暴露的服务：</p> 
<p>可以通过/metrics查看mysql_up指标判断当前MySQLD Exporter是否正常连接到了MySQL实例，当指标值为1时表示能够正常获取监控数据：</p> 
<pre><code># HELP mysql_up Whether the MySQL server is up.
# TYPE mysql_up gauge
mysql_up 1
</code></pre> 
<p>修改Prometheus配置文件/etc/prometheus/prometheus.yml，增加对MySQLD Exporter实例的采集任务配置:</p> 
<pre><code>- job_name: mysqld
  static_configs:
  - targets:
    - localhost:9104
</code></pre> 
<p>启动Prometheus:</p> 
<pre><code>prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/data/prometheus
</code></pre> 
<p>通过Prometheus的状态页，可以查看当前Target的状态：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/8d9fc39591b285074e4c020ad2e812ae.png" alt="MySQLD Exporter实例状态"></p> 
<p>为了确保数据库的稳定运行，通常会关注一下四个与性能和资源利用率相关的指标：查询吞吐量、连接情况、缓冲池使用情况以及查询执行性能等。</p> 
<h4><a id="_250"></a>监控数据库吞吐量</h4> 
<p>对于数据库而言，最重要的工作就是实现对数据的增、删、改、查。为了衡量数据库服务器当前的吞吐量变化情况。在MySQL内部通过一个名为Questions的计数器，当客户端发送一个查询语句后，其值就会+1。可以通过以下MySQL指令查询Questions等服务器状态变量的值：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Questions";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Questions     | 1326  |
+---------------+-------+
1 row in set (0.00 sec)
</code></pre> 
<p>MySQLD Exporter中返回的样本数据中通过mysql_global_status_questions反映当前Questions计数器的大小：</p> 
<pre><code># HELP mysql_global_status_questions Generic metric from SHOW GLOBAL STATUS.
# TYPE mysql_global_status_questions untyped
mysql_global_status_questions 1016
</code></pre> 
<p>通过以下PromQL可以查看当前MySQL实例查询速率的变化情况，查询数量的突变往往暗示着可能发生了某些严重的问题，因此用于用户应该关注并且设置响应的告警规则，以及时获取该指标的变化情况：</p> 
<pre><code>rate(mysql_global_status_questions[2m])
</code></pre> 
<p>一般还可以从监控读操作和写操作的执行情况进行判断。通过MySQL全局状态中的Com_select可以查询到当前服务器执行查询语句的总次数：相应的，也可以通过Com_insert、Com_update以及Com_delete的总量衡量当前服务器写操作的总次数，例如，可以通过以下指令查询当前MySQL实例insert语句的执行次数总量：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Com_insert";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_insert    | 0     |
+---------------+-------+
1 row in set (0.00 sec)
</code></pre> 
<p>从MySQLD Exporter的/metrics返回的监控样本中，可以通过global_status_commands_total获取当前实例各类指令执行的次数：</p> 
<pre><code># HELP mysql_global_status_commands_total Total number of executed MySQL commands.
# TYPE mysql_global_status_commands_total counter
mysql_global_status_commands_total{command="admin_commands"} 0
mysql_global_status_commands_total{command="alter_db"} 0
mysql_global_status_commands_total{command="alter_db_upgrade"} 0
mysql_global_status_commands_total{command="select"} 10
mysql_global_status_commands_total{command="insert"} 2
mysql_global_status_commands_total{command="update"} 2
mysql_global_status_commands_total{command="delete"} 1
</code></pre> 
<p>用户可以通过以下PromQL查看当前MySQL实例写操作速率的变化情况：</p> 
<pre><code>sum(rate(mysql_global_status_commands_total{command=~"insert|update|delete"}[2m])) without (command)
</code></pre> 
<h4><a id="_316"></a>连接情况</h4> 
<p>在MySQL中通过全局设置max_connections限制了当前服务器允许的最大客户端连接数量。一旦可用连接数被用尽，新的客户端连接都会被直接拒绝。 因此当监控MySQL运行状态时，需要时刻关注MySQL服务器的连接情况。用户可以通过以下指令查看当前MySQL服务的max_connections配置：</p> 
<pre><code>mysql&gt; SHOW VARIABLES LIKE 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set (0.01 sec)
</code></pre> 
<p>MySQL默认的最大链接数为151。临时调整最大连接数，可以通过以下指令进行设置：</p> 
<pre><code>SET GLOBAL max_connections = 200;
</code></pre> 
<p>如果想永久化设置，则需要通过修改MySQL配置文件my.cnf，添加以下内容：</p> 
<pre><code>
max_connections = 200
</code></pre> 
<p>通过Global Status中的Threads_connected、Aborted_connects、Connection_errors_max_connections以及Threads_running可以查看当前MySQL实例的连接情况。</p> 
<p>例如，通过以下指令可以直接当前MySQL实例的连接数：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Threads_connected";
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 1     |
+-------------------+-------+
1 row in set (0.00 sec)
</code></pre> 
<p>当所有可用连接都被占用时，如果一个客户端尝试连接至MySQL，会出现“Too many connections(连接数过多)”错误，同时Connection_errors_max_connections的值也会增加。为了防止出现此类情况，你应该监控可用连接的数量，并确保其值保持在max_connections限制以内。同时如果Aborted_connects的数量不断增加时，说明客户端尝试连接到MySQL都失败了。此时可以通过Connection_errors_max_connections以及Connection_errors_internal分析连接失败的问题原因。</p> 
<p>下面列举了与MySQL连接相关的监控指标：</p> 
<ul><li>mysql_global_variables_max_connections： 允许的最大连接数；</li><li>mysql_global_status_threads_connected： 当前开放的连接；</li><li>mysql_global_status_threads_running：当前开放的连接；</li><li>mysql_global_status_aborted_connects：当前开放的连接；</li><li>mysql_global_status_connection_errors_total{error=“max_connections”}：由于超出最大连接数导致的错误；</li><li>mysql_global_status_connection_errors_total{error=“internal”}：由于系统内部导致的错误；</li></ul> 
<p>通过PromQL查询当前剩余的可用连接数：</p> 
<pre><code>mysql_global_variables_max_connections - mysql_global_status_threads_connected
</code></pre> 
<p>使用PromQL查询当前MySQL实例连接拒绝数：</p> 
<pre><code>mysql_global_status_aborted_connects
</code></pre> 
<h4><a id="_380"></a>监控缓冲池使用情况</h4> 
<p>MySQL默认的存储引擎InnoDB使用了一片称为缓冲池的内存区域，用于缓存数据表以及索引的数据。 当缓冲池的资源使用超出限制后，可能会导致数据库性能的下降，同时很多查询命令会直接在磁盘中执行，导致磁盘I/O不断攀升。 因此，应该关注MySQL缓冲池的资源使用情况，并且在合理的时间扩大缓冲池的大小可以优化数据库的性能。</p> 
<p>Innodb_buffer_pool_pages_total反映了当前缓冲池中的内存页的总页数。可以通过以下指令查看：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Innodb_buffer_pool_pages_total";
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| Innodb_buffer_pool_pages_total | 8191  |
+--------------------------------+-------+
1 row in set (0.02 sec)
</code></pre> 
<p>MySQLD Exporter通过以下指标返回缓冲池中各类内存页的数量：</p> 
<pre><code># HELP mysql_global_status_buffer_pool_pages Innodb buffer pool pages by state.
# TYPE mysql_global_status_buffer_pool_pages gauge
mysql_global_status_buffer_pool_pages{state="data"} 516
mysql_global_status_buffer_pool_pages{state="dirty"} 0
mysql_global_status_buffer_pool_pages{state="free"} 7675
mysql_global_status_buffer_pool_pages{state="misc"} 0
</code></pre> 
<p>Innodb_buffer_pool_read_requests记录了正常从缓冲池读取数据的请求数量。可以通过以下指令查看：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Innodb_buffer_pool_read_requests";
+----------------------------------+--------+
| Variable_name                    | Value  |
+----------------------------------+--------+
| Innodb_buffer_pool_read_requests | 797023 |
+----------------------------------+--------+
1 row in set (0.00 sec)
</code></pre> 
<p>MySQLD Exporter通过以下指标返回缓冲池中Innodb_buffer_pool_read_requests的值：</p> 
<pre><code># HELP mysql_global_status_innodb_buffer_pool_read_requests Generic metric from SHOW GLOBAL STATUS.
# TYPE mysql_global_status_innodb_buffer_pool_read_requests untyped
mysql_global_status_innodb_buffer_pool_read_requests 736711
</code></pre> 
<p>当缓冲池无法满足时，MySQL只能从磁盘中读取数据。Innodb_buffer_pool_reads即记录了从磁盘读取数据的请求数量。通常来说从内存中读取数据的速度要比从磁盘中读取快很多，因此，如果Innodb_buffer_pool_reads的值开始增加，可能意味着数据库的性能有问题。 可以通过以下只能查看Innodb_buffer_pool_reads的数量</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Innodb_buffer_pool_reads";
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| Innodb_buffer_pool_reads | 443   |
+--------------------------+-------+
1 row in set (0.00 sec)
</code></pre> 
<p>在MySQLD Exporter中可以通过以下指标查看Innodb_buffer_pool_reads的数量。</p> 
<pre><code># HELP mysql_global_status_innodb_buffer_pool_reads Generic metric from SHOW GLOBAL STATUS.
# TYPE mysql_global_status_innodb_buffer_pool_reads untyped
mysql_global_status_innodb_buffer_pool_reads 443
</code></pre> 
<p>通过以上监控指标，以及实际监控的场景，我们可以利用PromQL快速建立多个监控项。</p> 
<p>通过以下PromQL可以得到各个MySQL实例的缓冲池利用率。一般来说还需要结合Innodb_buffer_pool_reads的增长率情况来结合判断缓冲池大小是否合理：</p> 
<pre><code>(sum(mysql_global_status_buffer_pool_pages) by (instance) - sum(mysql_global_status_buffer_pool_pages{state="free"}) by (instance)) / sum(mysql_global_status_buffer_pool_pages) by (instance)
</code></pre> 
<p>也可以通过以下PromQL计算2分钟内磁盘读取请求次数的增长率的变化情况：</p> 
<pre><code>rate(mysql_global_status_innodb_buffer_pool_reads[2m])
</code></pre> 
<h4><a id="_467"></a>查询性能</h4> 
<p>MySQL还提供了一个Slow_queries的计数器，当查询的执行时间超过long_query_time的值后，计数器就会+1，其默认值为10秒，可以通过以下指令在MySQL中查询当前long_query_time的设置：</p> 
<pre><code>mysql&gt; SHOW VARIABLES LIKE 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.00 sec)
</code></pre> 
<p>通过以下指令可以查看当前MySQL实例中Slow_queries的数量：</p> 
<pre><code>mysql&gt; SHOW GLOBAL STATUS LIKE "Slow_queries";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 0     |
+---------------+-------+
1 row in set (0.00 sec)
</code></pre> 
<p>MySQLD Exporter返回的样本数据中，通过以下指标展示当前的Slow_queries的值：</p> 
<pre><code># HELP mysql_global_status_slow_queries Generic metric from SHOW GLOBAL STATUS.
# TYPE mysql_global_status_slow_queries untyped
mysql_global_status_slow_queries 0
</code></pre> 
<p>通过监控Slow_queries的增长率，可以反映出当前MySQL服务器的性能状态，可以通过以下PromQL查询Slow_queries的增长情况：</p> 
<pre><code>rate(mysql_global_status_slow_queries[2m])
</code></pre> 
<p>在MySQL中还可以通过安装response time插件，从而支持记录查询时间区间的统计信息。启动该功能后MySQLD Exporter也会自动获取到相关数据，从而可以细化MySQL查询响应时间的分布情况。 感兴趣的读者可以自行尝试。</p> 
<h3><a id="Blackbox_Exporter_519"></a>网络探测：Blackbox Exporter</h3> 
<p>在本章的前几个小节中我们主要介绍了Prometheus下如何进行白盒监控，我们监控主机的资源用量、容器的运行状态、数据库中间件的运行数据。 这些都是支持业务和服务的基础设施，通过白盒能够了解其内部的实际运行状态，通过对监控指标的观察能够预判可能出现的问题，从而对潜在的不确定因素进行优化。而从完整的监控逻辑的角度，除了大量的应用白盒监控以外，还应该添加适当的黑盒监控。黑盒监控即以用户的身份测试服务的外部可见性，常见的黑盒监控包括HTTP探针、TCP探针等用于检测站点或者服务的可访问性，以及访问效率等。</p> 
<p>黑盒监控相较于白盒监控最大的不同在于黑盒监控是以故障为导向当故障发生时，黑盒监控能快速发现故障，而白盒监控则侧重于主动发现或者预测潜在的问题。一个完善的监控目标是要能够从白盒的角度发现潜在问题，能够在黑盒的角度快速发现已经发生的问题。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/0c1f5193fb68c735eba9b6513aad55f2.png" alt="黑盒监控和白盒监控"></p> 
<h4><a id="Blackbox_Exporter_529"></a>使用Blackbox Exporter</h4> 
<p>Blackbox Exporter是Prometheus社区提供的官方黑盒监控解决方案，其允许用户通过：HTTP、HTTPS、DNS、TCP以及ICMP的方式对网络进行探测。用户可以直接使用go get命令获取Blackbox Exporter源码并生成本地可执行文件：</p> 
<pre><code>go get prometheus/blackbox_exporter
</code></pre> 
<p>运行Blackbox Exporter时，需要用户提供探针的配置信息，这些配置信息可能是一些自定义的HTTP头信息，也可能是探测时需要的一些TSL配置，也可能是探针本身的验证行为。在Blackbox Exporter每一个探针配置称为一个module，并且以YAML配置文件的形式提供给Blackbox Exporter。 每一个module主要包含以下配置内容，包括探针类型（prober）、验证访问超时时间（timeout）、以及当前探针的具体配置项：</p> 
<pre><code>  # 探针类型：http、 tcp、 dns、 icmp.
  prober: &lt;prober_string&gt;

  # 超时时间
  [ timeout: &lt;duration&gt; ]

  # 探针的详细配置，最多只能配置其中的一个
  [ http: &lt;http_probe&gt; ]
  [ tcp: &lt;tcp_probe&gt; ]
  [ dns: &lt;dns_probe&gt; ]
  [ icmp: &lt;icmp_probe&gt; ]
</code></pre> 
<p>下面是一个简化的探针配置文件blockbox.yml，包含两个HTTP探针配置项：</p> 
<pre><code>modules:
  http_2xx:
    prober: http
    http:
      method: GET
  http_post_2xx:
    prober: http
    http:
      method: POST
</code></pre> 
<p>通过运行以下命令，并指定使用的探针配置文件启动Blockbox Exporter实例：</p> 
<p><code>blackbox_exporter --config.file=/etc/prometheus/blackbox.yml</code></p> 
<p>启动成功后，就可以通过访问http://127.0.0.1:9115/probe?module=http_2xx&amp;target=baidu.com对baidu.com进行探测。这里通过在URL中提供module参数指定了当前使用的探针，target参数指定探测目标，探针的探测结果通过Metrics的形式返回：</p> 
<pre><code># HELP probe_dns_lookup_time_seconds Returns the time taken for probe dns lookup in seconds
# TYPE probe_dns_lookup_time_seconds gauge
probe_dns_lookup_time_seconds 0.011633673
# HELP probe_duration_seconds Returns how long the probe took to complete in seconds
# TYPE probe_duration_seconds gauge
probe_duration_seconds 0.117332275
# HELP probe_failed_due_to_regex Indicates if probe failed due to regex
# TYPE probe_failed_due_to_regex gauge
probe_failed_due_to_regex 0
# HELP probe_http_content_length Length of http content response
# TYPE probe_http_content_length gauge
probe_http_content_length 81
# HELP probe_http_duration_seconds Duration of http request by phase, summed over all redirects
# TYPE probe_http_duration_seconds gauge
probe_http_duration_seconds{phase="connect"} 0.055551141
probe_http_duration_seconds{phase="processing"} 0.049736019
probe_http_duration_seconds{phase="resolve"} 0.011633673
probe_http_duration_seconds{phase="tls"} 0
probe_http_duration_seconds{phase="transfer"} 3.8919e-05
# HELP probe_http_redirects The number of redirects
# TYPE probe_http_redirects gauge
probe_http_redirects 0
# HELP probe_http_ssl Indicates if SSL was used for the final redirect
# TYPE probe_http_ssl gauge
probe_http_ssl 0
# HELP probe_http_status_code Response HTTP status code
# TYPE probe_http_status_code gauge
probe_http_status_code 200
# HELP probe_http_version Returns the version of HTTP of the probe response
# TYPE probe_http_version gauge
probe_http_version 1.1
# HELP probe_ip_protocol Specifies whether probe ip protocol is IP4 or IP6
# TYPE probe_ip_protocol gauge
probe_ip_protocol 4
# HELP probe_success Displays whether or not the probe was a success
# TYPE probe_success gauge
probe_success 1
</code></pre> 
<p>从返回的样本中，用户可以获取站点的DNS解析耗时、站点响应时间、HTTP响应状态码等等和站点访问质量相关的监控指标，从而帮助管理员主动的发现故障和问题。</p> 
<h4><a id="Prometheus_617"></a>与Prometheus集成</h4> 
<p>接下来，只需要在Prometheus下配置对Blockbox Exporter实例的采集任务即可。最直观的配置方式：</p> 
<pre><code>- job_name: baidu_http2xx_probe
  params:
    module:
    - http_2xx
    target:
    - baidu.com
  metrics_path: /probe
  static_configs:
  - targets:
    - 127.0.0.1:9115
- job_name: prometheus_http2xx_probe
  params:
    module:
    - http_2xx
    target:
    - prometheus.io
  metrics_path: /probe
  static_configs:
  - targets:
    - 127.0.0.1:9115
</code></pre> 
<p>这里分别配置了名为baidu_http2x_probe和prometheus_http2xx_probe的采集任务，并且通过params指定使用的探针（module）以及探测目标（target）。</p> 
<p>那问题就来了，假如我们有N个目标站点且都需要M种探测方式，那么Prometheus中将包含N * M个采集任务，从配置管理的角度来说显然是不可接受的。 在服务发现与Relabel”小节，我们介绍了Prometheus的Relabeling能力，这里我们也可以采用Relabling的方式对这些配置进行简化，配置方式如下：</p> 
<pre><code>scrape_configs:
  - job_name: 'blackbox'
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
        - http://prometheus.io    # Target to probe with http.
        - https://prometheus.io   # Target to probe with https.
        - http://example.com:8080 # Target to probe with http on port 8080.
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115
</code></pre> 
<p>这里针对每一个探针服务（如http_2xx）定义一个采集任务，并且直接将任务的采集目标定义为我们需要探测的站点。在采集样本数据之前通过relabel_configs对采集任务进行动态设置。</p> 
<ul><li>第1步，根据Target实例的地址，写入<code>__param_target</code>标签中。<code>__param_&lt;name&gt;</code>形式的标签表示，在采集任务时会在请求目标地址中添加<code>&lt;name&gt;</code>参数，等同于params的设置；</li><li>第2步，获取__param_target的值，并覆写到instance标签中；</li><li>第3步，覆写Target实例的<code>__address__</code>标签值为BlockBox Exporter实例的访问地址。</li></ul> 
<p>通过以上3个relabel步骤，即可大大简化Prometheus任务配置的复杂度:</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/e07c52c3cf81d3f42087dc8f06af5223.png" alt="Blackbox Target实例"></p> 
<p>接下来，我们将详细介绍Blackbox中常用的HTTP探针使用方式</p> 
<h4><a id="HTTP_681"></a>HTTP探针</h4> 
<p>HTTP探针是进行黑盒监控时最常用的探针之一，通过HTTP探针能够网站或者HTTP服务建立有效的监控，包括其本身的可用性，以及用户体验相关的如响应时间等等。除了能够在服务出现异常的时候及时报警，还能帮助系统管理员分析和优化网站体验。</p> 
<p>在上一小节讲过，Blockbox Exporter中所有的探针均是以Module的信息进行配置。如下所示，配置了一个最简单的HTTP探针：</p> 
<pre><code>modules:
  http_2xx_example:
    prober: http
    http:
</code></pre> 
<p>通过prober配置项指定探针类型。配置项http用于自定义探针的探测方式，这里有没对http配置项添加任何配置，表示完全使用HTTP探针的默认配置，该探针将使用HTTP GET的方式对目标服务进行探测，并且验证返回状态码是否为2XX，是则表示验证成功，否则失败。</p> 
<h5><a id="HTTP_698"></a>自定义HTTP请求</h5> 
<p>HTTP服务通常会以不同的形式对外展现，有些可能就是一些简单的网页，而有些则可能是一些基于REST的API服务。 对于不同类型的HTTP的探测需要管理员能够对HTTP探针的行为进行更多的自定义设置，包括：HTTP请求方法、HTTP头信息、请求参数等。对于某些启用了安全认证的服务还需要能够对HTTP探测设置相应的Auth支持。对于HTTPS类型的服务还需要能够对证书进行自定义设置。</p> 
<p>如下所示，这里通过method定义了探测时使用的请求方法，对于一些需要请求参数的服务，还可以通过headers定义相关的请求头信息，使用body定义请求内容：</p> 
<pre><code>http_post_2xx:
    prober: http
    timeout: 5s
    http:
      method: POST
      headers:
        Content-Type: application/json
      body: '{}'
</code></pre> 
<p>如果HTTP服务启用了安全认证，Blockbox Exporter内置了对basic_auth的支持，可以直接设置相关的认证信息即可：</p> 
<pre><code>http_basic_auth_example:
    prober: http
    timeout: 5s
    http:
      method: POST
      headers:
        Host: "login.example.com"
      basic_auth:
        username: "username"
        password: "mysecret"
</code></pre> 
<p>对于使用了Bear Token的服务也可以通过bearer_token配置项直接指定令牌字符串，或者通过bearer_token_file指定令牌文件。</p> 
<p>对于一些启用了HTTPS的服务，但是需要自定义证书的服务，可以通过tls_config指定相关的证书信息：</p> 
<pre><code> http_custom_ca_example:
    prober: http
    http:
      method: GET
      tls_config:
        ca_file: "/certs/my_cert.crt"
</code></pre> 
<h5><a id="_747"></a>自定义探针行为</h5> 
<p>在默认情况下HTTP探针只会对HTTP返回状态码进行校验，如果状态码为2XX（200 &lt;= StatusCode &lt; 300）则表示探测成功，并且探针返回的指标probe_success值为1。</p> 
<p>如果用户需要指定HTTP返回状态码，或者对HTTP版本有特殊要求，如下所示，可以使用valid_http_versions和valid_status_codes进行定义：</p> 
<pre><code>  http_2xx_example:
    prober: http
    timeout: 5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2"]
      valid_status_codes: []
</code></pre> 
<p>默认情况下，Blockbox返回的样本数据中也会包含指标probe_http_ssl，用于表明当前探针是否使用了SSL：</p> 
<pre><code># HELP probe_http_ssl Indicates if SSL was used for the final redirect
# TYPE probe_http_ssl gauge
probe_http_ssl 0
</code></pre> 
<p>而如果用户对于HTTP服务是否启用SSL有强制的标准。则可以使用fail_if_ssl和fail_if_not_ssl进行配置。fail_if_ssl为true时，表示如果站点启用了SSL则探针失败，反之成功。fail_if_not_ssl刚好相反。</p> 
<pre><code>  http_2xx_example:
    prober: http
    timeout: 5s
    http:
      valid_status_codes: []
      method: GET
      no_follow_redirects: false
      fail_if_ssl: false
      fail_if_not_ssl: false
</code></pre> 
<p>除了基于HTTP状态码，HTTP协议版本以及是否启用SSL作为控制探针探测行为成功与否的标准以外，还可以匹配HTTP服务的响应内容。使用fail_if_matches_regexp和fail_if_not_matches_regexp用户可以定义一组正则表达式，用于验证HTTP返回内容是否符合或者不符合正则表达式的内容。</p> 
<pre><code>  http_2xx_example:
    prober: http
    timeout: 5s
    http:
      method: GET
      fail_if_matches_regexp:
        - "Could not connect to database"
      fail_if_not_matches_regexp:
        - "Download the latest version here"
</code></pre> 
<p>最后需要提醒的时，默认情况下HTTP探针会走IPV6的协议。 在大多数情况下，可以使用preferred_ip_protocol=ip4强制通过IPV4的方式进行探测。在Bloackbox响应的监控样本中，也会通过指标probe_ip_protocol，表明当前的协议使用情况：</p> 
<pre><code># HELP probe_ip_protocol Specifies whether probe ip protocol is IP4 or IP6
# TYPE probe_ip_protocol gauge
probe_ip_protocol 6
</code></pre> 
<p>除了支持对HTTP协议进行网络探测以外，Blackbox还支持对TCP、DNS、ICMP等其他网络协议，感兴趣的读者可以从Blackbox的Github项目中获取更多使用信息</p>
                </div>
            </body>
            </html>
            